//============================================================================
// Name        : InformaticsDaemon.cpp
// Author      : vrandik
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================

#include <iostream>
#include <string>
#include "EjudgeLogonService.h"

#include <boost/asio.hpp>
#include <boost/thread/thread.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/array.hpp>
#include <boost/bind.hpp>

using namespace std;
using boost::asio::local::stream_protocol;


class session
  : public boost::enable_shared_from_this<session>
{
public:
  session(boost::asio::io_service& io_service, EjudgeLogonService &ej_service)
    : socket_(io_service), ejudge_logon_service_(ej_service)
  {
  }

  stream_protocol::socket& socket()
  {
    return socket_;
  }

  void start()
  {
    // Wait for request.
    socket_.async_read_some(boost::asio::buffer(&data_, sizeof(data_)),
        boost::bind(&session::handle_read,
          shared_from_this(), boost::asio::placeholders::error,
          boost::asio::placeholders::bytes_transferred));
  }

  void handle_read(const boost::system::error_code& error,
      size_t bytes_transferred)
  {
    if (!error)
    {
        LogonEntry entry(data_);
        std::cerr << entry.login_ << " " << entry.password_ << " " << entry.ip_ << " " << entry.contest_id_ << std::endl;
        LogonResult res;
        try {
            if (data_.type_ == LogonEntrySocket::client) {
                res = ejudge_logon_service_.getLogon(LogonEntry(entry.user_id_, entry.login_, entry.password_, entry.ip_, entry.contest_id_));
            } else {
                res = ejudge_logon_service_.getMasterLogon(LogonEntry(entry.user_id_, entry.login_, entry.password_, entry.ip_, entry.contest_id_));
            }
        }
        catch (EjudgeLogonServiceException e)
        {
            std::cerr << e.message_ << std::endl;
            res.sid_ = "";
            res.user_id_ = 0;
            res.name_ = e.message_;
        }

        // Send result.
        LogonResultSocket resSocket = res.getSocketEntry();
        boost::asio::async_write(socket_, boost::asio::buffer(&resSocket, sizeof(resSocket)),
              boost::bind(&session::handle_write,
              shared_from_this(), boost::asio::placeholders::error));
    }
  }

  void handle_write(const boost::system::error_code& error)
  {
    if (!error)
    {
        socket_.async_read_some(boost::asio::buffer(&data_, sizeof(data_)),
            boost::bind(&session::handle_read,
            shared_from_this(), boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred));
    }
  }

private:
  // The socket used to communicate with the client.
  stream_protocol::socket socket_;
  EjudgeLogonService &ejudge_logon_service_;
  LogonEntrySocket data_;
};

typedef boost::shared_ptr<session> session_ptr;

class EjudgeService
{
public:
    EjudgeService(boost::asio::io_service& io_service, Settings &settings)
    : io_service_(io_service),
      acceptor_(io_service_, stream_protocol::endpoint(settings.out_socket_path_)),
      ejudge_logon_service_(settings)

  {
        session_ptr new_session(new session(io_service_, ejudge_logon_service_));
        acceptor_.async_accept(new_session->socket(),
            boost::bind(&EjudgeService::handle_accept, this, new_session,
              boost::asio::placeholders::error));
  }

    void handle_accept(session_ptr new_session,
        const boost::system::error_code& error)
    {
      if (!error)
      {
        new_session->start();
        new_session.reset(new session(io_service_, ejudge_logon_service_));
        acceptor_.async_accept(new_session->socket(),
            boost::bind(&EjudgeService::handle_accept, this, new_session,
              boost::asio::placeholders::error));
      }
    }

private:
  boost::asio::io_service &io_service_;
  stream_protocol::acceptor acceptor_;
  EjudgeLogonService ejudge_logon_service_;
};

void run(boost::asio::io_service* io_service, Settings *settings)
{
  try
  {
    EjudgeService service(*io_service, *settings);
    io_service->run();
  }
  catch (std::exception& e)
  {
    std::cerr << "Exception in thread: " << e.what() << "\n";
    std::exit(1);
  }
}

int main(int argc, char *argv[])
{
    boost::asio::io_service io_serv;
//    stream_protocol::socket socket(io_serv);

    try
    {
        if (argc < 2)
        {
            std::cerr << "config's filename must be in arg" << endl;
            std::exit(1);
        }
        Settings settings(argv[1]);
        if (access(settings.out_socket_path_.c_str(), F_OK) == 0) {
            unlink(settings.out_socket_path_.c_str());
        }
        boost::thread thread(boost::bind(run, &io_serv, &settings));


        std::string login, password, ip;
        int contest_id, user_id;
        boost::asio::io_service io_service_test;

        for (;;)
        {
            pause();
/*            std::cout << "Enter a string: ";
            std::cin >> user_id >> login >> ip >> password >> contest_id;

            stream_protocol::socket s(io_service_test);
            s.connect(stream_protocol::endpoint(settings.out_socket_path_));
            // Collect request from user.
            LogonEntry entry(user_id, login, password, ip, contest_id);
            LogonEntrySocket entrySocket = entry.getSocketEntry();
            // Send request to filter.
            boost::asio::write(s, boost::asio::buffer(&entrySocket, sizeof(entrySocket)));

            // Wait for reply from filter.
            LogonResultSocket reply;
            boost::asio::read(s, boost::asio::buffer(&reply, sizeof(reply)));

            // Show reply to user.
            std::cout << "Result: ";
            std::cout << reply.name_ << " " << reply.sid_ << " " << reply.user_id_;
            std::cout << std::endl;*/
        }
    }
    catch (std::exception& e)
    {
        std::cerr << "Exception: " << e.what() << "\n";
        std::exit(1);
    }
    return 0;
}
